<...has anyone written an sprintf compatible hack for ST-80?>
This works in Tek Smalltalk. I have no idea how it behaves elsewhere.
------ cut here, and at end ------
'From Tektronix Smalltalk-80 version TB2.2.2a of May 05, 1988, 18:14:03. on 5 April 1989 at 12:28:07 pm'!
"The following methods implement printf and scanf functionality. They are intended to be used to ease porting between Smalltalk and C, and for facilitating machine-machine communication. They are not at all intended as replacements for Smalltalk's printOn: functionality."!
'$Header: PrintfScanf.st,v 1.1 89/05/08 10:00:33 jans Exp $'!
(Character canUnderstand: #to:) ifFalse:
[self == nil
ifFalse: [self requires: 'CharacterComparing.st']
ifTrue: [self notify: 'This code requires the file "CharacterComparing.st"']]!
!String methodsFor: 'printing'!
formatArgCount
"Return the number of arguments required/produced if the receiver is interpreted as a printf/scanf format control string."
"Format and print the receiver with <args> formatted in C style, as described in the UTek manual page for printf(3)."
| aStream |
aStream _ WriteStream on: String new.
self printOn: aStream withData: args.
^aStream contents!
printOn: outStream withData: args
"Format and print the receiver on <outStream> with <args> formatted in C style, as described in the UTek manual page for printf(3). This method is designed for producing output suitable for a machine. Hack 'printArgFrom:to:withData:' to use 'Character value: 25' instead of space if you want numeric columns to line up nicely."
| argStream inStream char |
argStream _ ReadStream on: args.
inStream _ ReadStream on: self.
[inStream atEnd] whileFalse:
[(char _ inStream next) == $%
ifFalse: [outStream nextPut: char]
ifTrue: [self printArgFrom: inStream to: outStream withData: argStream]]! !
!String methodsFor: 'converting'!
sscanf: string
"Return a Collection of objects found in <string> as interpreted according to the receiver. The receiver is assumed to be a conversion control string as specified in the UTek manual page for scanf(3)."
^self scanf: (ReadStream on: string)!
scanf: dataStream
"Return a Collection of objects found in the Character Stream <dataStream> as interpreted according to the receiver. The receiver is assumed to be a conversion control string as specified in the UTek manual page for scanf(3)."
| results format char |
results _ OrderedCollection new.
format _ ReadStream on: self.
[format atEnd] whileFalse:
[char _ format next.
(char == Character space or: [char == Character tab]) ifTrue:
[dataStream skipSeparators. format skipSeparators].
char == $%
ifTrue: [self scanArgFrom: dataStream to: results format: format]
ifFalse: [dataStream peekFor: char]].
^results! !
!String methodsFor: 'private'!
printArgFrom: inStream to: outStream withData: argStream
"Interpret the required number of arguments from <argStream> according to the formatting information in <inStream>. Place the interpretation on <outStream>. The interpretation is C printf(3) style, as described in the UTek manual page for printf(3). <inStream> is assumed to be positioned just past $%, and a complete control string is assumed available.
Return when the conversion control string is consumed. Leave <inStream> pointing past the last character in the conversion control string.
This code assumes that <inStream> is formatted according to specification, and error checking is minimal. Unexpected results will be obtained by illegal control strings, or when argument types do not match conversion codes, but it probably won't dump core, like C does in such cases!!"
| ljust plus pound width precision pad char arg argString sci |
ljust ifFalse: [outStream nextPutAll: (arg copyFrom: 1 to: precision)].
^inStream next!
scanArgFrom: dataStream to: collection format: format
"Add to <collection> an object who's representation is found in <dataStream> interpreted according to the conversion control string in the Stream <format>. <format> is assumed to be positioned just past a $%, and a complete control string is assumed available.
Return when the conversion control string is consumed. Leave <format> pointing past the last character in the conversion control string, leave <dataStream> pointing past any width specified in <format>, or at the first character that doesn't make sense for the <format>."
| final width char pos data scanset exclusive return last |
final _ [:retval |
collection add: retval.
data == dataStream ifFalse:
[dataStream position: dataStream position + data position].
^self].
width _ 0.
char _ format peek.
char == $% ifTrue: [^dataStream peekFor: char].
char == $* ifTrue:
[format next.
char _ format peek.
final _ [:retval |
data == dataStream ifFalse:
[dataStream position: dataStream position + data position].